import {DefaultForm, FormItemConfig, GlobelConfig, RenderTypeItem} from './ConfigType'
import typeOfUtils from "../../utils/type-of-utils";
import {DynamicForm} from "./index.vue.d";
import {VNode, VNodeData, VNodeDirective} from "vue/types/vnode";
import {CreateElement} from "vue/types/vue";

// @ts-ignore
const onlySelf = require.context('./RenderType', false, /.(js|ts)$/)


/**
 * 内置组件
 * @type {RenderTypeItem[]}
 */
const builtInComponents = onlySelf.keys().map(key => onlySelf(key).default)

class RenderUtils {
  static initValueOne(vm: DynamicForm, item: FormItemConfig) {
    if (!item.type || item.type === 'slot') {
      if (typeOfUtils.isFunction(item.initSlotData)) {
        // @ts-ignore
        item.initSlotData(vm.formData, item)
      }
      return;
    }
    // @ts-ignore
    if (typeOfUtils.isEmpty(vm.formData[item.prop])) {
      RenderUtils.initFormValue(vm, item)
    }
  }

  /**
   * 增强校验规则，添加一个同步的校验
   * @param vm
   * @param item
   */
  static rewriteRules(vm: DynamicForm, item: FormItemConfig) {
    if (item.rules) {
      item.rules.forEach(i => {
        // @ts-ignore
        if (i.validator && !i._old_validator) {
          // @ts-ignore
          i._old_validator = i.validator
        }
        if (i.validatorAsync) {
          i.validator = (rule, value, callback) => {
            // @ts-ignore
            const res = i.validatorAsync(rule, value, vm.formData);
            if (typeOfUtils.isPromise(res)) {
              (res as Promise<string>).then(r => {
                callback()
              }).catch(e => {
                callback(new Error(e))
              })
            } else if (typeOfUtils.isError(res)) {
              callback(res as Error)
            } else {
              callback()
            }
          }
          // @ts-ignore
        } else if (i._old_validator) {
          i.validator = (rule, value, callback) => {
            // @ts-ignore
            i._old_validator(rule, value, callback, vm.formData)
          }
        }
      })
    }
  }

  static initFormItemConfigOne(vm: DynamicForm, item: FormItemConfig) {
    RenderUtils.rewriteRules(vm, item)
    const find = builtInComponents.find(i => {
      return i.name === item.type
    })
    if (find && typeOfUtils.isFunction(find.initFormItem)) {
      find.initFormItem(vm, item)
    }
    const find1 = GlobelConfig.externalComponents.find(i => {
      return i.name === item.type
    })
    if (find1 && typeOfUtils.isFunction(find1.initFormItem)) {
      // @ts-ignore
      find1.initFormItem(vm, item)
    }
  }

  static formItemForEach(vm: DynamicForm, callBack: (item: FormItemConfig) => void) { // 添加formItems支持用函数返回
    vm.formConfig.formItems.forEach(item => {
      if (typeOfUtils.isFunction(item)) { // 添加formItems支持用函数返回
        // @ts-ignore
        item(vm.formData).forEach(item1 => {
          callBack(item1)
        })
      } else {
        // @ts-ignore
        callBack(item)
      }
    })
  }

  /**
   *
   * @param createElement
   * @param vm
   * @param formItem {FormItemConfig}
   * @return {VNode[]}
   */
  static typeRender(createElement, vm, formItem) {
    const find = builtInComponents.find(i => {
      return i.name === formItem.type
    })
    if (find) {
      return find.render(createElement, vm, formItem)
    }
    const find1 = GlobelConfig.externalComponents.find(i => {
      return i.name === formItem.type
    })
    if (find1) {
      return find1.render(createElement, vm, formItem)
    }
    if (typeOfUtils.isFunction(formItem?.render)) {
      return formItem.render(createElement, formItem, vm.formData)
    }
    // 使用作用域插槽渲染 名字为slotName
    if (vm.$scopedSlots[formItem.slotName]) {
      return vm.$scopedSlots[formItem.slotName](formItem, vm.formData)
    }
    // 使用作用域插槽渲染 名字为prop
    if (vm.$scopedSlots[formItem.prop]) {
      return vm.$scopedSlots[formItem.prop](formItem, vm.formData)
    }
  }

  /**
   *
   * @param createElement
   * @param vm
   * @param formItem
   */
  static renderFormItem(createElement: CreateElement, vm, formItem: FormItemConfig): VNode[] {
    if (typeOfUtils.isFunction(formItem?.noFormItem)) {
      // @ts-ignore
      return formItem.noFormItem(createElement, formItem, vm.formData)
    }
    // 使用作用域插槽渲染 名字为slotName+'NoFormItem' 外部不渲染el-form-item
    if (vm.$scopedSlots[formItem.slotName + 'NoFormItem']) {
      return vm.$scopedSlots[formItem.slotName + 'NoFormItem'](formItem, vm.formData)
    }
    // 使用作用域插槽渲染 名字为prop+'NoFormItem' 外部不渲染el-form-item
    if (vm.$scopedSlots[formItem.prop + 'NoFormItem']) {
      return vm.$scopedSlots[formItem.prop + 'NoFormItem'](formItem, vm.formData)
    }
    return [createElement('el-form-item', {
      props: {
        ...formItem,
      },
      // @ts-ignore
      key: formItem.prop + formItem.type,
      scopedSlots: {
        label: (props) => RenderUtils.renderFormItemLabel(vm, formItem, createElement, props),
        default: () => {
          return RenderUtils.typeRender(createElement, vm, formItem)
        },
      }
    })]
  }

  static renderFormItemLabel(vm: DynamicForm, formItem: FormItemConfig, createElement: CreateElement, props) {
    const labelWidth = formItem.labelWidth || (vm as DynamicForm).formConfig.labelWidth
    if (labelWidth === '0' || labelWidth === '0px') {
      return [
        createElement('div', {
          style: {
            width: 0,
            visibility: 'hidden',
            'pointer-events': 'none',
          }
        }, ['0'])
      ]
    }
    if (typeOfUtils.isFunction(formItem?.labelSlot)) {
      // @ts-ignore
      return formItem.labelSlot(createElement, props, formItem, (vm.labelAppend || DefaultForm.labelAppend))
    }
    if (vm.$scopedSlots[formItem.prop + 'Label']) {
      // @ts-ignore
      return vm.$scopedSlots[formItem.prop + 'Label']({
        props,
        formItem,
        labelAppend: (vm.labelAppend || DefaultForm.labelAppend)
      })
    }
    if (formItem?.label) {
      return formItem.label + (vm.labelAppend || DefaultForm.labelAppend)
    }
    return ''
  }

  /**
   * 渲染不是作用的域插槽
   * @param formItem {FormItemConfig}
   * @param createElement
   * @param slotNames {string[]}
   * @param vm
   * @return {VNode[]}
   */
  static renderSlotsChildren(formItem, createElement, slotNames, vm) {
    const children = []
    if (!slotNames) {
      return children
    }
    let typeSlots
    if (typeOfUtils.isFunction(formItem.typeSlots)) {
      typeSlots = formItem.typeSlots(createElement, formItem, vm.formData);
    }
    slotNames.forEach(slotName => {
      if (typeOfUtils.isNotEmpty(typeSlots) && typeOfUtils.isFunction(typeSlots[slotName])) {
        // @ts-ignore
        children.push(createElement('template', {
          slot: slotName
        }, typeSlots[slotName]()))
      } else {
        const slot = vm.$scopedSlots[formItem.prop + '-' + slotName]
        if (typeOfUtils.isFunction(slot)) {
          // @ts-ignore
          children.push(createElement('template', {
            slot: slotName
          }, slot({item: formItem, formData: vm.formData})))
        }
      }
    })
    return children
  }

  /**
   * 渲染不是作用的域插槽
   * @param formItem {FormItemConfig}
   * @param createElement
   * @param scopedSlotsNames {string[]}
   * @param vm
   * @return {{}}
   */
  static renderScopedSlots(formItem, createElement, scopedSlotsNames, vm) {
    const scopedSlots = {}
    if (!scopedSlotsNames) {
      return scopedSlots
    }
    let typeSlots
    if (typeOfUtils.isFunction(formItem.typeSlots)) {
      typeSlots = formItem.typeSlots(createElement, formItem, vm.formData);
    }
    scopedSlotsNames.forEach(slotName => {
      if (typeOfUtils.isNotEmpty(typeSlots) && typeOfUtils.isFunction(typeSlots[slotName])) {
        scopedSlots[slotName] = typeSlots[slotName]
      } else {
        const slot = vm.$scopedSlots[formItem.prop + '-' + slotName]
        if (typeOfUtils.isFunction(slot)) {
          scopedSlots[slotName] = (props) => {
            return slot({item: formItem, props, formData: vm.formData})
          }
        }
      }
    })
    return scopedSlots
  }

  /**
   *
   * @param vm
   * @param formItem {FormItemConfig}
   * @returns {{expression: string, callback: callback, value: *}}
   */
  static getModelConfig(vm, formItem) {
    let value = vm.formData[formItem.prop]
    if (typeOfUtils.isFunction(formItem.getModel)) {
      value = formItem.getModel(vm.formData[formItem.prop])
    }
    return {
      value,
      callback: (val) => {
        if (typeOfUtils.isFunction(formItem.setModel)) {
          val = formItem.setModel(val)
        }
        vm.$set(vm.formData, formItem.prop, val)
      },
      expression: 'formData.' + formItem.prop, // 不知道这个有什么作用，看编译出来的对value的不要前面的vm实例
    }
  }

  /**
   * 存放完全一样的config配置
   * @param vm
   * @param formItem
   * @param config
   * @param renderType
   */
  static getConfig(
    vm: DynamicForm,
    formItem: FormItemConfig,
    config: VNodeData,
    renderType: RenderTypeItem
  ): VNodeData {
    return {
      ref: RenderUtils.formItemToRef(formItem),
      refInFor: true,
      // @ts-ignore
      model: RenderUtils.getModelConfig(vm, formItem),
      props: {
        ...formItem.props,
        ...config.props,
        disabled: RenderUtils.setDisabled(formItem, vm)
      },
      directives: [
        RenderUtils.getTitle(vm, formItem, renderType.getTitle),
        ...(config.directives || [])
      ],
      attrs: {
        ...formItem.props,
        ...config.attrs,
        title: undefined,
      },
      on: {
        ...formItem.events,
        'hook:created': () => {
          formItem.events?.['hook:created'] && formItem.events['hook:created']()
        },
        ...config.on
      },
      nativeOn: {
        ...formItem.events,
        ...config.nativeOn
      }
    }
  }

  /**
   * 增强disabled
   * @param formItem
   * @param vm
   * @return {boolean}
   */
  static setDisabled(formItem: FormItemConfig, vm: DynamicForm) {
    if (typeOfUtils.isFunction(formItem.props?.setDisabled)) {
      // @ts-ignore
      const disabled = formItem.props.setDisabled(vm.formData, formItem) || false
      vm.$set(formItem.props, 'disabled', disabled)
      return disabled
    }
    if (typeOfUtils.isBoolean(formItem.props?.disabled)) {
      // @ts-ignore
      return formItem.props.disabled
    }
    if (typeOfUtils.isBoolean(vm.formConfig.disabled)) {
      return vm.formConfig.disabled
    }
    if (typeOfUtils.isBoolean(vm.$attrs.disabled)) {
      return vm.$attrs.disabled
    }
    return false
  }

  /**
   * 增加选项里面disabled
   * @param optionsItem
   * @param vm
   * @return {*|boolean}
   */
  static setOptionsDisabled(optionsItem, vm: DynamicForm) {
    if (vm.$attrs.disabled) {
      return true
    }
    if (typeOfUtils.isFunction(optionsItem.setDisabled)) {
      const disabled = optionsItem.setDisabled(vm.formData, optionsItem) || false
      vm.$set(optionsItem, 'disabled', disabled)
      return disabled
    }
    return optionsItem.disabled || false
  }

  /**
   * 设置隐藏
   * @param formItem {FormItemConfig}
   * @param vm
   * @returns {boolean}
   */
  static setHidden(formItem, vm) {
    if (typeOfUtils.isFunction(formItem.setHidden)) {
      const hidden = formItem.setHidden(vm.formData, formItem) || false
      vm.$set(formItem, 'hidden', hidden)
      return hidden
    }
    return formItem.hidden || false
  }

  /**
   *  隐藏元素，但是有占位
   * @param formItem {FormItemConfig}
   * @param vm
   * @returns {boolean}
   */
  static setOpacity(formItem, vm) {
    if (typeOfUtils.isFunction(formItem.setOpacity)) {
      const opacity = formItem.setOpacity(vm.formData, formItem)
      vm.$set(formItem, 'opacity', opacity)
      return opacity
    }
    return formItem.opacity
  }

  static initFormValue(vm, formItem) {
    let initValue = formItem.initValue
    if (typeOfUtils.isFunction(formItem.setInitValue)) {
      initValue = formItem.setInitValue()
    }
    vm.$set(vm.formData, formItem.prop, initValue);
    if (typeOfUtils.isNotEmpty(initValue)) {
      return
    }
    const find = builtInComponents.find(i => {
      return i.name === formItem.type
    })
    if (find && typeOfUtils.isFunction(find.initFormValue)) {
      find.initFormValue(vm, formItem)
      return
    }
    const find1 = GlobelConfig.externalComponents.find(i => {
      return i.name === formItem.type
    })
    if (find1 && typeOfUtils.isFunction(find1.initFormValue)) {
      // @ts-ignore
      find1.initFormValue(vm, formItem)
    }
  }

  /**
   * formItem到ref
   * @param formItem {FormItemConfig}
   */
  static formItemToRef(formItem) {
    if (formItem.ref) {
      return formItem.ref
    }
    return formItem.prop + 'Ref'
  }

  /**
   *
   * @param formItems {FormItemConfig[]}
   * @param prop
   * @returns {FormItemConfig}
   */
  static propToFormItem(formItems, prop) {
    return formItems.find(i => i.prop === prop)
  }

  static getComponents() {
    const result = {}
    /**
     * @type {RenderTypeItem[]}
     */
    const list = [...builtInComponents, ...GlobelConfig.externalComponents]
    list.forEach(i => {
      if (typeOfUtils.isNotEmpty(i.components)) {
        Object.keys(i.components).forEach(key => {
          result[key] = i.components[key]
        })
      }
    })
    return result
  }

  /**
   * 显示组件标题
   * @param vm
   * @param formItem
   * @param getTitle
   */
  static getTitle(vm, formItem, getTitle): VNodeDirective {
    if (typeOfUtils.isFunction(formItem.props?.getShowTitle)) { // 判断有没有显示显示标题，有直接调用，否则走组件默认的功能
      const showTitle = formItem.props.getShowTitle(vm.formData, formItem);
      if (typeOfUtils.isEmpty(showTitle)) { // 如果是空直接返回
        // @ts-ignore
        return {}
      }
      vm.$set(formItem.props, 'title', showTitle)
      return {
        name: 'showTooltip',
        value: {
          value: showTitle,
          everyShow: true
        },
      }
    }
    // if (typeOfUtils.isNotBoolean(formItem.props?.title)) { // 不是boolean直接返回
    //   if (typeOfUtils.isEmpty(formItem.props?.title)) { // 如果是空直接返回
    //     return {}
    //   }
    //   return {
    //     name: 'showTooltip',
    //     value: {
    //       value: formItem.props.title,
    //       everyShow: true
    //     },
    //   }
    // }
    if (formItem.props?.title === false) { // 如果自己定义为false也不显示
      // @ts-ignore
      return {}
    }
    if (vm.formConfig.showTitle && DefaultForm.showTitle) { // 判断全局是否显示
      if (typeOfUtils.isFunction(getTitle)) {
        return {
          name: 'showTooltip',
          value: {
            value: getTitle(vm, formItem), // 调用组件自己的显示标题方法
            everyShow: true
          },
        }
      }
    }
    // @ts-ignore
    return {}
  }

  /**
   * 同步返回props里面的options和setOptions
   */
  static getItemPropsOptions(formItem: FormItemConfig) {
    // @ts-ignore
    let options = formItem.props.options || []
    // @ts-ignore
    if (typeOfUtils.isEmpty(options) && typeOfUtils.isFunction(formItem.props.setOptions)) {
      // @ts-ignore
      options = formItem.props.setOptions() || []
      // @ts-ignore 记录setOptions返回的值
      formItem.props._setOptions = options
    }
    return options
  }
}

export default RenderUtils
